﻿using dnAnalytics.LinearAlgebra;
using dnAnalytics.Math;
using dnAnalytics.Tests.LinearAlgebra;
using NUnit.Framework;

namespace dnAnalytics.Tests
{
    public abstract class ApplyVectorTests
    {
        protected abstract Vector GetVector(double[] data);

        [Test]
        public void CanApplyMapToVector()
        {
            var data = GetVector(new[] {1.0, 2.0, 3.0});
            data.Map(x => 1/x);
            Assert.That(data[0], Is.EqualTo(1));
            Assert.That(data[1], Is.EqualTo(.5));
            Assert.That(data[2], Is.EqualTo(1.0/3.0));
        }


        [Test]
        public void CanApplyPowToVector()
        {
            var data = GetVector(new[] {1.0, 2.0, 3.0});
            data.Pow(3.0);
            Assert.That(data[0], Is.EqualTo(1));
            Assert.That(data[1], Is.EqualTo(8));
            Assert.That(data[2], Is.EqualTo(27));
        }

        [Test]
        public void CanApplySqrtMapFunctionToVector()
        {
            var data = GetVector(new[] {1.0, 2.0, 3.0});
            data.Map(System.Math.Sqrt);
            Assert.That(data[0], Is.EqualTo(System.Math.Sqrt(1.0)));
            Assert.That(data[1], Is.EqualTo(System.Math.Sqrt(2.0)));
            Assert.That(data[2], Is.EqualTo(System.Math.Sqrt(3.0)));
        }

        /*[Test]
        public void CanApplyInvertMapFunctionToVector()
        {
            var data = GetVector(new[] {1.0, 2.0, 3.0});
            data.Map(MapFunction.Invert);
            Assert.That(data[0], Is.EqualTo(1));
            Assert.That(data[1], Is.EqualTo(.5));
            Assert.That(data[2], Is.EqualTo(1.0/3.0));
        }*/

        [Test]
        public void CanApplyAbsMapFunctionToVector()
        {
            var data = GetVector(new[] {-1.0, -2.0, -3.0});
            data.Map(System.Math.Abs);
            Assert.That(data[0], Is.EqualTo(1.0));
            Assert.That(data[1], Is.EqualTo(2.0));
            Assert.That(data[2], Is.EqualTo(3.0));
        }

        [Test]
        public void CanApplyExpMapFunctionToVector()
        {
            var data = GetVector(new[] {1.0, 2.0, 3.0});
            data.Map(System.Math.Exp);
            Assert.That(data[0], Is.EqualTo(System.Math.Exp(1.0)));
            Assert.That(data[1], Is.EqualTo(System.Math.Exp(2.0)));
            Assert.That(data[2], Is.EqualTo(System.Math.Exp(3.0)));
        }

        [Test]
        public void CanApplyLog10MapFunctionToVector()
        {
            var data = GetVector(new[] {1.0, 2.0, 3.0});
            data.Map(System.Math.Log10);
            Assert.That(data[0], Is.EqualTo(System.Math.Log10(1.0)));
            Assert.That(data[1], Is.EqualTo(System.Math.Log10(2.0)));
            Assert.That(data[2], Is.EqualTo(System.Math.Log10(3.0)));
        }

        [Test]
        public void CanApplyLnMapFunctionToVector()
        {
            var data = GetVector(new[] {1.0, 2.0, 3.0});
            data.Map(System.Math.Log);
            Assert.That(data[0], Is.EqualTo(System.Math.Log(1.0)));
            Assert.That(data[1], Is.EqualTo(System.Math.Log(2.0)));
            Assert.That(data[2], Is.EqualTo(System.Math.Log(3.0)));
        }

        [Test]
        public void CanApplySinMapFunctionToVector()
        {
            var data = GetVector(new[] {1.0, 2.0, 3.0});
            data.Map(System.Math.Sin);
            Assert.That(data[0], Is.EqualTo(System.Math.Sin(1.0)));
            Assert.That(data[1], Is.EqualTo(System.Math.Sin(2.0)));
            Assert.That(data[2], Is.EqualTo(System.Math.Sin(3.0)));
        }

        [Test]
        public void CanApplyCosMapFunctionToVector()
        {
            var data = GetVector(new[] {1.0, 2.0, 3.0});
            data.Map(System.Math.Cos);
            Assert.That(data[0], Is.EqualTo(System.Math.Cos(1.0)));
            Assert.That(data[1], Is.EqualTo(System.Math.Cos(2.0)));
            Assert.That(data[2], Is.EqualTo(System.Math.Cos(3.0)));
        }

        [Test]
        public void CanApplyTanMapFunctionToVector()
        {
            var data = GetVector(new[] {1.0, 2.0, 3.0});
            data.Map(System.Math.Tan);
            Assert.That(data[0], Is.EqualTo(System.Math.Tan(1.0)));
            Assert.That(data[1], Is.EqualTo(System.Math.Tan(2.0)));
            Assert.That(data[2], Is.EqualTo(System.Math.Tan(3.0)));
        }

        [Test]
        public void CanApplyAsinMapFunctionToVector()
        {
            var data = GetVector(new[] {1.0, 2.0, 3.0});
            data.Map(System.Math.Asin);
            Assert.That(data[0], Is.EqualTo(System.Math.Asin(1.0)));
            Assert.That(data[1], Is.EqualTo(System.Math.Asin(2.0)));
            Assert.That(data[2], Is.EqualTo(System.Math.Asin(3.0)));
        }

        [Test]
        public void CanApplyAcosMapFunctionToVector()
        {
            var data = GetVector(new[] {1.0, 2.0, 3.0});
            data.Map(System.Math.Acos);
            Assert.That(data[0], Is.EqualTo(System.Math.Acos(1.0)));
            Assert.That(data[1], Is.EqualTo(System.Math.Acos(2.0)));
            Assert.That(data[2], Is.EqualTo(System.Math.Acos(3.0)));
        }

        [Test]
        public void CanApplyATanMapFunctionToVector()
        {
            var data = GetVector(new[] {1.0, 2.0, 3.0});
            data.Map(System.Math.Atan);
            Assert.That(data[0], Is.EqualTo(System.Math.Atan(1.0)));
            Assert.That(data[1], Is.EqualTo(System.Math.Atan(2.0)));
            Assert.That(data[2], Is.EqualTo(System.Math.Atan(3.0)));
        }

        [Test]
        public void CanApplySinhMapFunctionToVector()
        {
            var data = GetVector(new[] {1.0, 2.0, 3.0});
            data.Map(System.Math.Sinh);
            Assert.That(data[0], Is.EqualTo(System.Math.Sinh(1.0)));
            Assert.That(data[1], Is.EqualTo(System.Math.Sinh(2.0)));
            Assert.That(data[2], Is.EqualTo(System.Math.Sinh(3.0)));
        }

        [Test]
        public void CanApplyCoshMapFunctionToVector()
        {
            var data = GetVector(new[] {1.0, 2.0, 3.0});
            data.Map(System.Math.Cosh);
            Assert.That(data[0], Is.EqualTo(System.Math.Cosh(1.0)));
            Assert.That(data[1], Is.EqualTo(System.Math.Cosh(2.0)));
            Assert.That(data[2], Is.EqualTo(System.Math.Cosh(3.0)));
        }

        [Test]
        public void CanApplyTanhMapFunctionToVector()
        {
            var data = GetVector(new[] {1.0, 2.0, 3.0});
            data.Map(System.Math.Tanh);
            Assert.That(data[0], Is.EqualTo(System.Math.Tanh(1.0)));
            Assert.That(data[1], Is.EqualTo(System.Math.Tanh(2.0)));
            Assert.That(data[2], Is.EqualTo(System.Math.Tanh(3.0)));
        }

        [Test]
        public void CanApplyErfMapFunctionToVector()
        {
            var data = GetVector(new[] {1.0, 2.0, 3.0});
            data.Map(SpecialFunctions.Erf);
            Assert.That(data[0], Is.EqualTo(SpecialFunctions.Erf(1.0)));
            Assert.That(data[1], Is.EqualTo(SpecialFunctions.Erf(2.0)));
            Assert.That(data[2], Is.EqualTo(SpecialFunctions.Erf(3.0)));
        }

        [Test]
        public void CanApplyErfcMapFunctionToVector()
        {
            var data = GetVector(new[] {1.0, 2.0, 3.0});
            data.Map(SpecialFunctions.Erfc);
            Assert.That(data[0], Is.EqualTo(SpecialFunctions.Erfc(1.0)));
            Assert.That(data[1], Is.EqualTo(SpecialFunctions.Erfc(2.0)));
            Assert.That(data[2], Is.EqualTo(SpecialFunctions.Erfc(3.0)));
        }

        [Test]
        public void CanApplyErfInvMapFunctionToVector()
        {
            var data = GetVector(new[] {1.0, 2.0, 3.0});
            data.Map(SpecialFunctions.ErfInv);
            Assert.That(data[0], Is.EqualTo(SpecialFunctions.ErfInv(1.0)));
            Assert.That(data[1], Is.EqualTo(SpecialFunctions.ErfInv(2.0)));
            Assert.That(data[2], Is.EqualTo(SpecialFunctions.ErfInv(3.0)));
        }

        [Test]
        public void CanApplyErfcInvMapFunctionToVector()
        {
            var data = GetVector(new[] {.1, .2, .3});
            data.Map(SpecialFunctions.ErfcInv);
            Assert.That(data[0], Is.EqualTo(SpecialFunctions.ErfcInv(.1)).Within(10).Ulps);
            Assert.That(data[1], Is.EqualTo(SpecialFunctions.ErfcInv(.2)).Within(10).Ulps);
            Assert.That(data[2], Is.EqualTo(SpecialFunctions.ErfcInv(.3)).Within(10).Ulps);
        }
    }

    [TestFixture]
    public class ApplyDenseVectorTests : ApplyVectorTests
    {
        protected override Vector GetVector(double[] data)
        {
            return new DenseVector(data);
        }
    }

    [TestFixture]
    public class ApplySparseVectorTests : ApplyVectorTests
    {
        protected override Vector GetVector(double[] data)
        {
            return new SparseVector(data);
        }
    }

    [TestFixture]
    public class ApplyBaseVectorTests : ApplyVectorTests
    {
        protected override Vector GetVector(double[] data)
        {
            return new BaseVector(data);
        }
    }
}